// Author: Adwait Jog


#include "l1_prefetcher.h"

extern std::vector<mem_fetch_t*> prefetch_sent_requests[64];
extern unsigned long long int to_be_prefetched[32]; 
extern unsigned g_next_request_uid;
extern int gpu_concentration;
extern char *gpgpu_cache_dl2_opt;
extern long int gpgpu_l1_prefetch_late;
extern long int gpgpu_prefetch_init;
extern dram_t **dram;
extern int gpgpu_fetch_group;

void l1_prefetcher_cleaner() 
{
	for (int j = 0; j < 32; j++) {
		to_be_prefetched[j] = 0;
	}
}

void l1_prefetcher_logic(unsigned long long int addr) 
{
	// decide which ones to prefetch and put in to_be_prefetched
	// all prefetching logic goes here
	// what to prefetch, how much to prefetcher will be assited by the scheduler
	
	// usefulness
	// cache contention
	// warp memory waiting time
	
	to_be_prefetched[0] = addr + 128*gpgpu_fetch_group; // next group prefetching
	
	//to_be_prefetched[1] = addr + 128;

}

mem_fetch_t* prepare_prefetch_request(unsigned long long int addr, int bsize, 
						   int sid, int wid, enum mem_access_type mem_acc, address_type pc)
{ 
   mem_fetch_t *mf;
   mem_fetch_t *prefetch_access;

   mf = (mem_fetch_t*) calloc(1,sizeof(mem_fetch_t));
   mf->request_uid = g_next_request_uid++;
   mf->addr = addr;
   mf->nbytes_L1 = bsize;
   mf->sid = sid;
   mf->source_node = sid / gpu_concentration;
   mf->wid = wid;
   mf->cache_hits_waiting = 0;
   mf->txbytes_L1 = 0;
   mf->rxbytes_L1 = 0;  
   mf->mshr = NULL;
   mf->write = false;
   mf->l1_prefetched = true;
   mf->there_was_demand = false;
   mf->type = RD_REQ; 
   
   addrdec_tlx(addr,&mf->tlx);
   mf->bank = mf->tlx.bk;
   mf->chip = mf->tlx.chip;
   if (gpgpu_cache_dl2_opt)
      mf->nbytes_L2 = L2c_get_linesize( dram[mf->tlx.chip] );
   else
      mf->nbytes_L2 = 0;
   mf->txbytes_L2 = 0;
   mf->rxbytes_L2 = 0;  

   mf->write_mask = NO_PARTIAL_WRITE;
  
   mf->mem_acc = mem_acc;
   mf->pc = pc;
   prefetch_access = mf;  
  return prefetch_access;
}

bool check_in_prefetch_queues(mem_fetch_t* mf, bool from_degree, unsigned long long int addr) 
{
	for(int i=0; i < (int)prefetch_sent_requests[mf->sid].size(); i++) {
	  if(prefetch_sent_requests[mf->sid].at(i)->addr == addr) {
			if (from_degree) {
				return true;
			}
			else if(prefetch_sent_requests[mf->sid].at(i)->there_was_demand == false) {
				gpgpu_l1_prefetch_late++;
				// add code for distribution
				//prefetch_sent_requests[sid].at(i)->mshr = mshr;
				prefetch_sent_requests[mf->sid].at(i) = mf;
				prefetch_sent_requests[mf->sid].at(i)->there_was_demand = true;
				prefetch_sent_requests[mf->sid].at(i)->write = false;
				prefetch_sent_requests[mf->sid].at(i)->l1_prefetched = true;
				//printf("yes:, there is demand for (sent) = %x, coreid = %d\n", mf->addr, mf->sid);
				//printf("yes:, are you there? %x, coreid = %d\n", prefetch_sent_requests[mf->sid].at(i)->addr, mf->sid);
				return true;
			}								
		}
	}
	return false;
}

void * check_in_demand_waiting_queue(unsigned long long int addr, int sid) 
{
	//printf("searching for = %x\n", addr);
	//printf("size = %d\n", prefetch_sent_requests[sid].size());
	for(int i=0; i < (int)prefetch_sent_requests[sid].size(); i++) {
	   if(prefetch_sent_requests[sid].at(i)->addr == addr) {
				//printf("confirm:, address matched (sent) = %x\n", addr);
			if (prefetch_sent_requests[sid].at(i)->there_was_demand == true) {
				//printf("confirm:, there is demand for (sent) = %x\n", addr);
				return prefetch_sent_requests[sid].at(i);
			}
		}
	}
	return NULL;
}

void remove_from_sent_queue(unsigned long long int addr, int sid) {
	for(int i=0; i < (int) prefetch_sent_requests[sid].size(); i++) {
		if (prefetch_sent_requests[sid].at(i)->there_was_demand == true) {
		   //printf(" PLEASE SERVE ME = %x\n", prefetch_sent_requests[mf->sid].at(i)->addr);
		}
		if(prefetch_sent_requests[sid].at(i)->addr == addr) {
			//printf("i m deleting addr = %x, mf-sid = %d, sc-sid = %d\n", mf->addr, mf->sid, sc->sid); 
			//printf("Vector size is : %d\n",prefetch_sent_requests[mf->sid].size());
			prefetch_sent_requests[sid].erase(prefetch_sent_requests[sid].begin() + i);
			//printf("Vector size is : %d\n",prefetch_sent_requests[mf->sid].size());
		}
	}
}
